﻿using ACC.Business.Stn;
using ACC.Business.Utils;
using ACC.Data;
using ACC.Data.ACCLog;
using ACC.Data.Comm;
using ACC.Data.Line;
using ACC.Device;
using ACC.Device.PLC;
using ACC.Device.PLC.Siemens;
using ACC.Util.Convert;
using ACC.Interface.Extensions;
using ACC.LineDevice.KukaPack;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;


/// <summary> PL01_模组基体
///           主线逻辑：OP L1M01060前电芯单个检测，在OP L1M01060中以时间顺序检索6个电芯绑定一个托盘，扫托盘码，单独load（unload）每个电芯码，以第一个isOK信号作为整个托盘的OK/NG判断
///           OP L1M03020中在load时通过WEBAPI获得一个模组基体码，load绑定N个电芯条码，unload1个模组基体码   
///           有模组基体码的工站中向MES进行报工
///           L1M03080 三元电芯侧板焊接（预留），取消
/// </summary>
namespace Script
{
    public class PL02_JT : LineProcessKukaPack
    {
        public PL02_JT(LineData data)
            : base(data)
        {
            restfulUrl = "http://127.0.0.1:4002/api/KukaPack/";
        }

        /// <summary>
        /// 初始化ACC连接PLC使用的Driver配置
        /// </summary>
        protected override void InitDriver()
        {
            base.InitDriver();

            /* 
             * TO-DO: 以下代码用于设定使用虚拟PLC,调试完成删除
             */

            ////获取在Config中配置的Device对应的Driver
            PLCDriver driver = (PLCDriver)LineDeviceDrivers["PLC20"];
            driver.SetBaseDriver(new VSiemensDriver_Base(driver.DeviceCfg));
            driver = (PLCDriver)LineDeviceDrivers["PLC22"];
            driver.SetBaseDriver(new VSiemensDriver_Base(driver.DeviceCfg));
            driver = (PLCDriver)LineDeviceDrivers["PLC112"];
            driver.SetBaseDriver(new VSiemensDriver_Base(driver.DeviceCfg));
            driver = (PLCDriver)LineDeviceDrivers["PLC111"];
            driver.SetBaseDriver(new VSiemensDriver_Base(driver.DeviceCfg));
            driver = (PLCDriver)LineDeviceDrivers["PLC120"];
            driver.SetBaseDriver(new VSiemensDriver_Base(driver.DeviceCfg));
            //driver = (PLCDriver)LineDeviceDrivers["L1M13031"];
            //driver.SetBaseDriver(new VSiemensDriver_Base(driver.DeviceCfg));
            //driver = (PLCDriver)LineDeviceDrivers["PLCTest"];
            //driver.SetBaseDriver(new VSiemensDriver_Base(driver.DeviceCfg));                                                                                                                                                           
        }
    }

    /// <summary>
    /// 处理模组基体基类
    /// </summary>
    public class StnProcessPL02_JT : StnProcessKukaPack
    {
        public StnProcessPL02_JT(StnRunningData data)
            : base(data)
        {
        }

        protected override ACCDevice CreateDevice()
        {
            ACCDevice dev = base.CreateDevice();
            if (kukaSiemensDev != null)
            {
                kukaSiemensDev.PartTypeDataIntLength = 20;
                kukaSiemensDev.PartTypeDataIntLength = 50;
                kukaSiemensDev.PartTypeDataStringLength = 0;
            }

            return dev;
        }

    }

    /// <summary>
    /// 处理电芯的基类
    /// </summary>
    public class StnProcessPL02_DX : StnProcessKukaPack
    {
        public StnProcessPL02_DX(StnRunningData data)
            : base(data)
        {
            IsReport = false;
            IsACCReport = false;
            IsPalletRelationSN = false;
        }

        protected override ACCDevice CreateDevice()
        {
            ACCDevice dev = base.CreateDevice();
            if (kukaSiemensDev != null)
            {
                kukaSiemensDev.PartTypeDataIntLength = 20;
                kukaSiemensDev.PartTypeDataIntLength = 50;
                kukaSiemensDev.PartTypeDataStringLength = 0;
            }

            return dev;
        }

        protected override void CreateBusinessLogic()
        {
            base.CreateBusinessLogic();

            _loadProcess.Uninit();
            _loadProcess = new LoadProcess_DX(_data);

            _unloadProcess.Uninit();
            _unloadProcess = new UnloadProcess_DX(_data);

            _nextStepProcess.Uninit();
            _nextStepProcess = new NextStepProcessKukaPack(_data);
        }
    }

    /// <summary>
    /// 一次处理多个条码(电芯/绝缘片) Load
    /// </summary>
    public class LoadProcess_DX : LoadProcess
    {
        public LoadProcess_DX(StnRunningData data)
            : base(data)
        {
        }
        //每个电芯/绝缘片数据个数
        public int R = 1;

        /// <summary>
        /// API 修改成BOMItem电芯数量为1后不用在重写这个方法
        /// 由于每个电芯码/绝缘片都是单独的主件，需要屏蔽掉Bom中的条码数量检测
        /// </summary>
        /// <param name="loadSNs"></param>
        /// <param name="CompCfg"></param>
        /// <param name="param"></param>
        /// <param name="result"></param>
        protected override void CheckComponentCount(List<String> loadSNs, BomItemCfg[] CompCfg, ACCDevice.LoadParam param, ref ACCDevice.LoadResult result)
        {
            //int compCount = (int)CompCfg.Sum(c => c.QtyPer <= 0 ? 1 : c.QtyPer);
            //if (loadSNs.Count != compCount)
            //{
            //    //先检查长度 
            //    result.ErrorCode = ACCCode.ACCOK;
            //}
        }

        /// <summary>
        /// 1.设备一次提交6个电芯的测试数据，6个电芯条码 + 6组电芯测试数据；
        /// 2.如果设备没有扫描电芯条码，也会进行测试，针对电芯条码为空的情况，设备标记条码内容为空/“NG"条码,数据也不需要记录,ACC提示空条码；
        /// 3.PLC将每个电芯的测试结果（NO/NG）记录在processdata中，ACC根据该数据来设置每个电芯的statusbit；
        /// </summary>
        /// <param name="dev"></param>
        public override void ProcessLoad(ACCDevice dev)
        {
            try
            {
                string[] barcodes = new string[] { _loadParam.UnitSN };
                var v = barcodes.Concat(_loadParam.ComponentSN);
                _loadParam.ComponentSN = new string[] { };
                _loadParam.PartNo = _data.PartNo;
                _loadParam.PartTypeID = _data.PartNo == null ? 0 : (int)_data.PartNo.BomCfg.PartTypeID;

                // 先检查每个电芯/绝缘片的状态，通过PreLoad （NextStep），然后才Load（要么都成功，要么都失败，避免一些Load了一些没有的情况）
                //foreach (string unitsn in v)
                //{
                //    _loadParam.UnitSN = unitsn;

                //    if (string.IsNullOrEmpty(unitsn) || unitsn.ToUpper().Equals("NG"))
                //    {
                //        logger.Debug("电芯/绝缘片条码为空");
                //        _loadResult.ErrorCode = ACCCode.Unit_Barcode_Empty;
                //        return;
                //    }

                //    var param = new ACCDevice.NextStepParam();
                //    param.UnitSN = unitsn;
                //    param.PartTypeID = _data.PartNo == null ? 0 : (int)_data.PartNo.BomCfg.PartTypeID;
                //    param.PartNo = _data.PartNo;
                //    var rs = _stnProcess.nextStepProcess.PreLoad(param);
                //    if (rs != ACCCode.ACCOK)
                //    {
                //        _loadResult.ErrorCode = rs;
                //        return;
                //    }
                //}
                foreach (string unitsn in v)
                {
                    // 电芯线
                    if (_data.Route != null && _data.Route.Segment != null && _data.Route.Segment.SegmentCode == "1")
                    {
                        if (string.IsNullOrEmpty(unitsn) || string.IsNullOrEmpty(unitsn.Trim()) || unitsn.Trim().ToUpper() == "NG")
                        {
                            StnEventLog.Log(_data, StnLog.LogType.Warning, "电芯码为空");
                            continue;
                        }
                    }
                    _loadParam.UnitSN = unitsn;
                    base.ProcessLoad(dev);
                    if (_loadResult.ErrorCode == ACCCode.ACCOK)
                        (_stnProcess as StnProcessKukaPack).ReportIn(dev, _data, _loadResult);
                    else
                        return;
                }
            }
            catch (Exception ex)
            {
                logger.Debug(ex.Message + "\r\n" + ex.StackTrace);
            }
        }

        public override bool CheckUnitRoute(WIPUnit unit, ref ACCDevice.LoadResult result)
        {
            Object[] param;
            LoadProcessFunc.CheckUnitRoute(_data, unit, out result.ErrorCode, out param);

            switch (result.ErrorCode)
            {
                //case ACCCode.Load_RouteFail_UnitISFG:
                //case ACCCode.Load_RouteFail_UnitIsScrap:
                //case ACCCode.Load_RouteFail_UnitIsOnHold: 
                //    throw new ACCException(ErrorConstants.LOAD_UNIT_ERRORSTATUS, param);

                case ACCCode.Load_RouteFail_ReworkToOtherOP:
                    {
                        var ex = new ACCException(ErrorConstants.LOAD_UNIT_ERROR_RWK_OP, param);
                        ex.ErrorCode = result.ErrorCode;
                        ex.ErrorMessage = string.Empty;
                        throw ex;
                    }

                case ACCCode.Load_NotExist_UnitSN:
                    {
                        var ex = new ACCException(ErrorConstants.LOAD_UNIT_NOTFOUND, param);
                        ex.ErrorCode = result.ErrorCode;
                        ex.ErrorMessage = string.Empty;
                        throw ex;
                    }

                case ACCCode.Load_RouteFail_CurrentOPIsFinish:
                    {
                        var ex = new ACCException(ErrorConstants.LOAD_UNIT_CURROP_ISFINISH, _data.StnCfg.OPCfg.OP);
                        ex.ErrorCode = result.ErrorCode;
                        ex.ErrorMessage = string.Empty;
                        throw ex;
                    }

                case ACCCode.Load_RouteFail_WrongOP:
                    {
                        if (_data.StnCfg.OPCfg.OP == "L1M01030" ||
                            _data.StnCfg.OPCfg.OP == "L1M01040" ||
                            _data.StnCfg.OPCfg.OP == "L1M01060")
                        {
                            var ex = new ACCException(ErrorConstants.LOAD_UNIT_ERROR_OP, param);
                            ex.ErrorCode = result.ErrorCode;
                            ex.ErrorMessage = string.Empty;
                            throw ex;
                        }
                        // TO-DO: 电芯、端板、绝缘板暂时屏蔽WrongOP的检查
                        StnEventLog.Log(_data, StnLog.LogType.Event, "Load_RouteFail_WrongOP");
                        result.ErrorCode = ACCCode.ACCOK;
                        result.ErrorMsg = string.Empty;
                        break;
                    }

                case ACCCode.Load_RouteFail_PriorOPStatusIsWrong:
                    {
                        var ex = new ACCException(ErrorConstants.LOAD_UNIT_OPSTATUS_ERROR, param);
                        ex.ErrorCode = result.ErrorCode;
                        ex.ErrorMessage = string.Empty;
                        throw ex;
                    }
                default:
                    break;
            }

            return true;
        }
    }

    /// <summary>
    /// 一次处理多个条码(电芯/绝缘片) Unload
    /// </summary>
    public class UnloadProcess_DX : UnloadProcess
    {
        public UnloadProcess_DX(StnRunningData data)
            : base(data)
        {
        }

        //每个电芯/绝缘片REAL类型数据个数
        public int R = 1;
        //每个电芯String类型个数
        public int S = 0;
        // 结果里代表数据是否NG，一般情况下是1代表OK，0代表NG，但是有些PLC区域用1代表NG，0代表OK
        public bool OneAsNG = false;
        // 数据不多的情况下不使用线程保存数据，避免死锁
        public bool SaveToDBDirectly = false;

        /// <summary>
        /// API 修改成BOMItem电芯数量为1后不用在重写这个方法
        /// API 默认在L1M01030插入了6个数量
        /// </summary>
        /// <param name="loadSNs"></param>
        /// <param name="CompCfg"></param>
        /// <param name="param"></param>
        /// <param name="result"></param>
        protected override void CheckComponentCount(List<string> loadSNs, BomItemCfg[] CompCfg, ACCDevice.UnloadParam param, ref ACCDevice.UnloadResult result)
        {
            //int compCount = (int)CompCfg.Sum(c => c.QtyPer <= 0 ? 1 : c.QtyPer);
            //if (loadSNs.Count != compCount)
            //{
            //    result.ErrorCode = ACCCode.ACCOK;
            //}
        }

        /// <summary>
        /// 1.设备一次提交多个电芯/绝缘片的测试数据，多个电芯/绝缘片条码 + 多个电芯/绝缘片测试数据；
        /// 2.如果设备没有扫描电芯/绝缘片条码，也会进行测试，针对电芯/绝缘片条码为空的情况，设备标记条码内容为空；ACC不需要对NG条码的进行处理，数据也不需要记录；
        /// 3.PLC将每个电芯/绝缘片的测试结果（NO/NG，有些PLC用1代表NG，有些用0代表NG）记录在processdata中，ACC根据该数据来设置每个电芯的statusbit；
        /// </summary>
        /// <param name="dev"></param>
        public override void ProcessUnload(ACCDevice dev)
        {
            try
            {
                var en = _unloadParam.N_Real.GetEnumerator();
                var str = _unloadParam.N_String.GetEnumerator();
                _unloadParam.N_Real = new DictionaryEx<string, ACCDevice.ProcDataValue>();
                _unloadParam.N_String = new DictionaryEx<string, ACCDevice.ProcDataValue>();
                string[] barcodes = new string[] { _unloadParam.UnitSN };
                var v = barcodes.Concat(_unloadParam.ComponentSN);
                _unloadParam.ComponentSN = new string[] { };
                _unloadParam.PartNo = _data.PartNo;
                _unloadParam.PartTypeID = _data.PartNo == null ? 0 : (int)_data.PartNo.BomCfg.PartTypeID;
                foreach (string unitsn in v)
                {
                    
                    // 电芯线
                    if (_data.Route != null && _data.Route.Segment != null && _data.Route.Segment.SegmentCode == "1")
                    {
                        if (string.IsNullOrEmpty(unitsn) || string.IsNullOrEmpty(unitsn.Trim()) || unitsn.Trim().ToUpper() == "NG")
                        {
                            continue;
                        }
                    }

                    _unloadParam.UnitSN = unitsn;
                    _unloadParam.N_Real = new DictionaryEx<string, ACCDevice.ProcDataValue>();
                    _unloadParam.N_String = new DictionaryEx<string, ACCDevice.ProcDataValue>();
                    //if (string.IsNullOrEmpty(unitsn) || unitsn.ToUpper().Equals("NG"))
                    //{
                    //    logger.Debug("电芯/绝缘片条码为空");
                    //    _unloadResult.ErrorCode = ACCCode.Unit_Barcode_Empty;
                    //    return;
                    //}
                    // 根据电芯/绝缘片顺序获取电芯数据
                    for (int i = 0; i < R; i++)
                    {
                        if (en.MoveNext())
                        {
                            _unloadParam.N_Real.Add(en.Current.Key, en.Current.Value);

                        }
                    }
                    // 根据电芯顺序获取电芯涂胶时间（String）
                    for (int i = 0; i < S; i++)
                    {
                        if (str.MoveNext())
                        {
                            _unloadParam.N_String.Add(str.Current.Key, str.Current.Value);
                        }
                    }
                    // 解析每个电芯/绝缘片的结果
                    foreach (string itemKey in _unloadParam.N_Real.Keys)
                    {
                        if (itemKey.IndexOf("结果") >= 0)
                        {
                            double result = _unloadParam.N_Real[itemKey].value;
                            _unloadParam.IsOk = result > 0 ? true : false;
                            if (OneAsNG) _unloadParam.IsOk = !_unloadParam.IsOk;
                            _unloadParam.StatusBits = _unloadParam.IsOk ? (uint)1 : 0;
                            _unloadParam.FailureBits = _unloadParam.IsOk ? (uint)0 : 1;

                            break;
                        }
                    }
                    // 单个电芯/绝缘片处理并给MES报工
                    base.ProcessUnload(dev);
                    if (_unloadResult.ErrorCode == ACCCode.ACCOK)
                        (_stnProcess as StnProcessKukaPack).ReportOut(dev, _data, _unloadParam, _unloadResult);
                    else
                        return;
                }
            }
            catch (Exception ex)
            {
                logger.Debug(ex.Message + "\r\n" + ex.StackTrace);
            }
        }

        /// <summary>
        /// 取消线程保存数据（此站数据不多），直接保存数据库
        /// 避免死锁
        /// </summary>
        /// <param name="rec_id"></param>
        /// <param name="param"></param>
        protected override void SaveProcDatasEx(uint rec_id, ACCDevice.UnloadParam param)
        {
            if (!SaveToDBDirectly)
            {
                base.SaveProcDatasEx(rec_id, param);
            }
            else
            {
                try
                {
                    SaveProcDatas(rec_id, param);
                }
                catch (Exception ex)
                {
                    logger.Error("SaveProcDatasEx failed: \r\n", ex);
                }
            }
        }
    }

}
